/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.validator.internal;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import ch.trackedbean.validator.*;

/**
 * Class describing an mapping between an external {@link Annotation} and an {@link IPropertyValidator}.<br>
 * Use this mapping to "resuse" existing {@link Annotation}s for validation purposes.
 * 
 * @param <T> The annotation type
 * @param <V> The type of the validator
 * 
 * @author M. Hautle
 */
public class ExternalAnnotationMapping<T extends Annotation, V extends IPropertyValidator> {
    /** The destination validator. */
    protected final Class<V> validator;

    /** The external annotation to map on {@link #validator}. */
    protected final Class<T> annotation;

    /** List holding the parameter mapping. */
    protected final List<Parameter> params = new ArrayList<Parameter>(1);

    /**
     * Default constructor.
     * 
     * @param annotation The external annotation
     * @param validator The target validator
     */
    public ExternalAnnotationMapping(Class<T> annotation, Class<V> validator) {
        this.validator = validator;
        this.annotation = annotation;
    }

    /**
     * Returns the type of the external annotation.
     * 
     * @return The type of the annotation
     */
    public Class<T> getExternalAnnotationType() {
        return annotation;
    }

    /**
     * Returns the type of the validator to use.
     * 
     * @return Returns the validator type
     */
    public Class<V> getValidatorType() {
        return validator;
    }

    /**
     * Adds a parameter mapping between the annotation and a logical name used by the validator.
     * 
     * @param srcParam The parameter name on the external annotation
     * @param dstParam The parameter name as used by the validator
     * @throws NoSuchMethodException If the annotation has no such method
     */
    public void addParameter(String srcParam, String dstParam) throws NoSuchMethodException {
        params.add(new Parameter(annotation.getMethod(srcParam), dstParam));
    }

    /**
     * Copies the values from the passed annoation into the given configuration.
     * 
     * @param annotation The annotation
     * @param cfg The configuration
     * @throws ExternalConfigurationException If something went wrong
     */
    public void copyConfig(T annotation, Configuration cfg) throws ExternalConfigurationException {
        cfg.clear();
        for (Parameter p : params)
            p.copy(annotation, cfg);
    }

    /**
     * Parameter mapping between a {@link ExternalAnnotationMapping#annotation} parameter and it's name in the specified {@link IPropertyValidator}.
     * 
     * @author M. Hautle
     */
    protected static class Parameter {
        /** The parameter name used by the {@link IPropertyValidator}. */
        protected final String dstParam;

        /** The method to invoke on the {@link ExternalAnnotationMapping#annotation} to extract the value. */
        protected final Method srcParam;

        /**
         * Default constructor
         * 
         * @param srcParam The source parameter
         * @param dstParam The destination parameter name
         */
        protected Parameter(Method srcParam, String dstParam) {
            this.srcParam = srcParam;
            this.dstParam = dstParam;
        }

        /**
         * Copies the parameter from the given annotation into the passed configuration.
         * 
         * @param a The annotation
         * @param cfg The config
         * @throws ExternalConfigurationException If something went wrong
         */
        protected void copy(Annotation a, Configuration cfg) throws ExternalConfigurationException {
            try {
                cfg.addEntry(dstParam, srcParam.invoke(a));
            } catch (InvocationTargetException e) {
                throw new ExternalConfigurationException(e);
            } catch (IllegalAccessException e) {
                throw new ExternalConfigurationException(e);
            }
        }
    }

    /**
     * Excpetion thrown if an error occours during the configuration.
     * 
     * @author M. Hautle
     */
    public static class ExternalConfigurationException extends Exception {
        /**
         * Default constructor.
         * 
         * @param e The source exception
         */
        public ExternalConfigurationException(Exception e) {
            super(e);
        }
    }
}
